/**
  ******************************************************************************
  * @file    bms_analysis.c
  * @author  Forester
  * @brief   
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/

#include <stdio.h>
#include <math.h>

#include "bms_config.h"
#include "bms_global.h"

#include "bms_utils.h"
#include "bms_analysis.h"
#include "bms_monitor.h"
#include "bms_protect.h"

#include "bms_hal_control.h"

#include "freertos.h"
#include "task.h"

// thread config
#define ANALYSISI_TASK_STACK_SIZE	  128
#define ANALYSISI_TASK_PRIORITY		  3
#define ANALYSISI_TASK_PERIOD		    1000

#define TEMP_CAP_RATE_LIMITH_HIGH   1050
#define TEMP_CAP_RATE_LIMITL_LOW    750

/* Private functions ---------------------------------------------------------*/
static TaskHandle_t AnalysisTaskHandler = NULL; 

static void BMS_AnalysisTaskEntry(void *paramter);

static void BMS_AnalysisEasy( void );
static void BMS_AnalysisCalCap( void );
static void BMS_AnalysisSocCheck( void );
static void BMS_AnalysisCapAndSocInit( void );
static void BMS_AnalysisAHSocCalculate( void );
static void BMS_AnalysisOcvSocCalculate( void );
static uint16_t BMS_AnalysisOcvToSoc( uint16_t minCellVoltage );


/* Private variable ----------------------------------------------------------*/

// Open circuit voltage meter.
// Note: The voltage table obtained from online sources for lithium-ion batteries 
//       needs to be verified through specific battery experiments to establish 
//       the real voltage table.
static uint16_t SocOcvTab[101]=
{
	3282, // 0%~1%	
	3309, 3334, 3357, 3378, 3398, 3417, 3434, 3449, 3464, 3477,	// 0%~10%
	3489, 3500, 3510, 3520, 3528, 3536, 3543, 3549, 3555, 3561,	// 11%~20%
	3566, 3571, 3575, 3579, 3583, 3586, 3590, 3593, 3596, 3599,	// 21%~30%
	3602, 3605, 3608, 3611, 3615, 3618, 3621, 3624, 3628, 3632,	// 31%~40%
	3636, 3640, 3644, 3648, 3653, 3658, 3663, 3668, 3674, 3679,	// 41%~50%
	3685, 3691, 3698, 3704, 3711, 3718, 3725, 3733, 3741, 3748,	// 51%~60%
	3756, 3765, 3773, 3782, 3791, 3800, 3809, 3818, 3827, 3837,	// 61%~70%
	3847, 3857, 3867, 3877, 3887, 3897, 3908, 3919, 3929, 3940,	// 71%~80%
	3951, 3962, 3973, 3985, 3996, 4008, 4019, 4031, 4043, 4055,	// 81%~90%
	4067, 4080, 4092, 4105, 4118, 4131, 4145, 4158, 4172, 4185,	// 91~100%
};

/* Export variable -----------------------------------------------------------*/
BMS_AnalysisDataTypedef BMS_AnalysisData =
{
	.CapacityRated = BMS_BATTERY_CAPACITY,
};

/* Function defination -------------------------------------------------------*/
void BMS_AnalysisInit(void)
{
	BaseType_t res = xTaskCreate(BMS_AnalysisTaskEntry,
															 "analysis",
															 ANALYSISI_TASK_STACK_SIZE,
															 NULL,
															 ANALYSISI_TASK_PRIORITY,
															 &AnalysisTaskHandler);
	if(res != pdPASS)
	{
		printf("Create Analysis Task Fail\r\n");
	}
}

void BMS_AnalysisTaskEntry(void *paramter)
{
	BMS_AnalysisCapAndSocInit();
	while(1)
	{
		BMS_AnalysisEasy();
		BMS_AnalysisCalCap();
		BMS_AnalysisSocCheck();
		BMS_HALShowSocByLed(BMS_AnalysisData.SOC * 1000);
		vTaskDelay(pdMS_TO_TICKS(ANALYSISI_TASK_PERIOD));
	}
}


// 简单分析: 能直接通过采集数据计算得到
static void BMS_AnalysisEasy()
{
	uint8_t index = 0;
	
	// 压差
	BMS_AnalysisData.MaxVoltageDifference = BMS_MonitorData.CellData[BMS_GlobalParam.Cell_Real_Number - 1].CellVoltage - BMS_MonitorData.CellData[0].CellVoltage;
	
	// 平均压差WWW
	for(index = 0, BMS_AnalysisData.AverageVoltage = 0.0f; index < BMS_GlobalParam.Cell_Real_Number; index++)
	{
		BMS_AnalysisData.AverageVoltage += BMS_MonitorData.CellVoltage[index];
	}
	BMS_AnalysisData.AverageVoltage /= BMS_GlobalParam.Cell_Real_Number;
	
	// 实时功率: 总压 X 实时电流
	BMS_AnalysisData.PowerReal = BMS_MonitorData.BatteryVoltage * BMS_MonitorData.BatteryCurrent;
	
	// 最大和最小电压
	BMS_AnalysisData.CellVoltMax = BMS_MonitorData.CellData[BMS_GlobalParam.Cell_Real_Number - 1].CellVoltage;
	BMS_AnalysisData.CellVoltMin = BMS_MonitorData.CellData[0].CellVoltage;
}

/**
  * @brief  Calibrating battery capacity using temperature.
	*         At different temperatures, the chemical reaction rates of batteries change, which 
  *         directly affects their effective capacity and lifespan:
  *         1. Under low temperature conditions, the internal resistance of batteries increases, 
  *            leading to a decrease in available capacity.
  *         2. High temperatures may accelerate the chemical reactions in batteries, which might 
  *            seem to enhance performance in the short term, but can actually speed up battery aging 
  *            and capacity loss.
  *          
  */
static void BMS_AnalysisTempCal(void)
{
	uint16_t ratio;
	uint16_t diff = 0;
	uint16_t rateTemp = 0;
	
	static int16_t lastTemp = 0;
	
	// Calibrate using the lowest temperature collected.
	int16_t minTemp = BMS_MonitorData.CellTemp[0] * 10;
	
	if( BMS_MonitorData.CellTempEffectiveNumber == 0 )
	{
		return;
	}
	
	// Perform a capacity calibration whenever the rate of temperature change exceeds 1 degree.
	diff = minTemp - lastTemp;
	if( diff > -10 && diff < 10 )
	{
		return;
	}
	lastTemp = minTemp;
	
	/*
	 * Using 25 degrees Celsius as the standard:
	 * 
	 * >= 25 Celsius             : dincrease capacity by one thousandth per degree.
	 * >= 10  && < 25 Celsius    : decrease capacity by two thousandths per degree.
	 * >= 0   && < 10 Celsius    : decrease capacity by three thousandths per degree.
	 * >= -20 && < 0  Celsius    : decrease capacity by four thousandths per degree.
	 * >= -30 && < -20 Celsius   : decrease capacity by five thousandths per degree.
	 * For even lower temperatures, decrease capacity by six thousandths per degree.
	 * 
	*/	
	if( minTemp >= 250 )
	{
		ratio = 1;
	}
	else if( minTemp >= 100 && minTemp < 250 )
	{
		ratio = 2;
	}
	else if( minTemp >= 0 && minTemp < 100 )
	{
		ratio = 3;
	}
	else if( minTemp >= -200 && minTemp < 0)
	{
		ratio = 4;
	}
	else if( minTemp >= -300 && minTemp < -200 )
	{
		ratio = 5;
	}
	else
	{
		ratio = 6;
	}
	
	rateTemp = 1000 + ratio * ( minTemp - 250 ) / 10;
	
	// Set an upper and lower limit for capacity.
	rateTemp = rateTemp > TEMP_CAP_RATE_LIMITH_HIGH ? TEMP_CAP_RATE_LIMITH_HIGH : rateTemp;
	rateTemp = rateTemp < TEMP_CAP_RATE_LIMITL_LOW ? TEMP_CAP_RATE_LIMITL_LOW : rateTemp;
	
	BMS_AnalysisData.CapacityReal = BMS_AnalysisData.CapacityRated * rateTemp / 1000;
	BMS_AnalysisData.CapacityRemain = BMS_AnalysisData.CapacityReal * BMS_AnalysisData.SOC;
}

/**
  * @brief Calculate the real-time capacity of the battery by considering factors such 
  *        as temperature, complete charge-discharge cycles, and aging.
  *        
  *
  *
  * @note  For the current stage, only temperature will be used as a factor, since other 
  *        aspects are difficult to manage without specialized equipment and environment.
  */
static void BMS_AnalysisCalCap(void)
{	
	BMS_AnalysisTempCal();
}

/**
  * @brief When the device powers on, initialize the actual capacity and SOC of the battery.
  *        - When powered on, SOC is initially calculated using the open circuit voltage table.
  *        - The remain capacity = real capacity * soc.
  *
  * @note The actual capacity at power-on is temporarily assigned the nominal capacity,  
  *       as the system initially disregards factors such as battery aging.
  */
static void BMS_AnalysisCapAndSocInit(void)
{
	BMS_AnalysisData.SOC = BMS_AnalysisOcvToSoc(BMS_MonitorData.CellData[0].CellVoltage * 1000) / 1000.0;
	
	// Real capacity initialized to nominal capacity.
	BMS_AnalysisData.CapacityReal = BMS_AnalysisData.CapacityRated;
	
	BMS_AnalysisData.CapacityRemain = BMS_AnalysisData.CapacityReal * BMS_AnalysisData.SOC;
}

/**
  * @brief Include all the algorithms for SOC calculation. 
  *        
  *        Currently includes two algorithms: open circuit voltage method and ampere-hour
  *        integration method.
  *
  */
static void BMS_AnalysisSocCheck( void )
{
	// Calibrate battery SOC during bms sleep mode.
	BMS_AnalysisOcvSocCalculate();
	
	// Calibrate battery SOC during bms no-sleeo mode.
	BMS_AnalysisAHSocCalculate();
}

/**
  * @brief Get SOC obtained from the battery's total voltage measured by an open circuit
  *        voltage meter.
  *
  * @param[In] minCellVoltage : minimum single cell voltage, unit mv.
  *
  * @note Only called when battery in static mode (doesn't charge and discharge).
  */
static uint16_t BMS_AnalysisOcvToSoc(uint16_t minCellVoltage)
{
	uint16_t soc = 0;
	
	if( minCellVoltage <= SocOcvTab[0] )
	{
		soc = 0;
	}
	else if( minCellVoltage >= SocOcvTab[100] )
	{
		soc = 1000;
	}
	else
	{
		uint16_t index = right_bound(SocOcvTab, 0, 100, minCellVoltage);
		
		if( minCellVoltage == SocOcvTab[index] )
		{
			soc = index * 10;
		}
		else
		{
			soc = index * 10 + ( ( SocOcvTab[index] - minCellVoltage ) * 10 ) / ( SocOcvTab[index] - SocOcvTab[index + 1] );
		}
	}
	
	return soc;
}

/**
  * @brief When in battery standby mode, calibrate SOC using open circuit voltage method.
  *
  */
static void BMS_AnalysisOcvSocCalculate( void )
{
	if( BMS_GlobalParam.SysMode == BMS_MODE_SLEEP )
	{
		// Wait for a period of time for voltage stabilization. ( might after balance )
		vTaskDelay( pdMS_TO_TICKS( BALANCE_VOLT_RISE_DELAY ) );
	
		BMS_AnalysisData.SOC = BMS_AnalysisOcvToSoc( BMS_MonitorData.CellData[0].CellVoltage * 1000 ) / 1000.0;
		
		BMS_AnalysisData.CapacityRemain = BMS_AnalysisData.CapacityReal * BMS_AnalysisData.SOC;
	}
}

/**
  * @brief Calibrate battery SOC based on ampere-hour integration method during 
  *        non-sleep mode.
  *
  */
static void BMS_AnalysisAHSocCalculate( void )
{
	// A/S -> A/H
	float currentAh = abs( ( uint32_t ) (BMS_MonitorData.BatteryCurrent * 1000) ) / 1000.0 / 3600.0;
	
	if( BMS_MODE_STANDBY == BMS_GlobalParam.SysMode )
	{
		if( BMS_MonitorData.CellData[0].CellVoltage >= BMS_Protect.param.OVProtect )
		{
			BMS_AnalysisData.CapacityRemain = BMS_AnalysisData.CapacityReal;
		}
		else if( BMS_MonitorData.CellData[0].CellVoltage <= BMS_Protect.param.UVProtect )
		{
			BMS_AnalysisData.CapacityRemain = 0;
		}
	}
	else if( BMS_MODE_DISCHARGE == BMS_GlobalParam.SysMode )
	{
		if( BMS_AnalysisData.CapacityRemain >= currentAh )
		{
			BMS_AnalysisData.CapacityRemain -= currentAh;
		}
		else
		{
			BMS_AnalysisData.CapacityRemain = 0;
		}
	}
	else if( BMS_MODE_CHARGE == BMS_GlobalParam.SysMode )
	{
		if( BMS_AnalysisData.CapacityReal >= BMS_AnalysisData.CapacityRemain + currentAh )
		{
			BMS_AnalysisData.CapacityRemain += currentAh;
		}
		else
		{
			BMS_AnalysisData.CapacityRemain = BMS_AnalysisData.CapacityReal;
		}
	}
	
	BMS_AnalysisData.SOC = BMS_AnalysisData.CapacityRemain / BMS_AnalysisData.CapacityReal;
	if( BMS_AnalysisData.SOC > 1 )
	{
		BMS_AnalysisData.SOC = 1;
	}
}